import os
import sys
import sqlite3
import subprocess
import datetime
import shutil
import threading
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog, scrolledtext
import tkinter.font as tkfont
from tkinter import filedialog
import time

# 设置中文字体支持
def set_chinese_font(root):
    try:
        # 尝试设置中文字体
        if sys.platform == 'win32':
            default_font = tkfont.nametofont("TkDefaultFont")
            default_font.configure(family="SimHei", size=10)
            root.option_add("*Font", default_font)
    except:
        pass

class ScanThread(threading.Thread):
    """文件扫描线程"""
    def __init__(self, root_paths, extensions=None, callback=None):
        super().__init__()
        self.root_paths = root_paths
        self.extensions = extensions if extensions is not None else []
        self.running = True
        self.found_files = []
        self.callback = callback
        self.total_files = 0
    
    def run(self):
        for root_path in self.root_paths:
            if not self.running:
                break
                
            for dirpath, _, filenames in os.walk(root_path):
                if not self.running:
                    break
                    
                for filename in filenames:
                    if not self.running:
                        break
                        
                    # 检查文件扩展名是否在指定的列表中
                    if any(filename.lower().endswith(ext.lower()) for ext in self.extensions):
                        try:
                            full_path = os.path.join(dirpath, filename)
                            file_size = os.path.getsize(full_path)
                            # 获取文件创建时间
                            creation_time = os.path.getctime(full_path)
                            # 格式化为可读时间
                            formatted_time = datetime.datetime.fromtimestamp(creation_time).strftime('%Y-%m-%d %H:%M:%S')
                            
                            file_info = {
                                'name': filename,
                                'path': full_path,
                                'size': self.format_size(file_size),
                                'ext': filename.split('.')[-1].upper(),
                                'created': formatted_time
                            }
                            self.found_files.append(file_info)
                            # 回调处理找到的文件
                            if self.callback:
                                self.callback(file_info)
                        except Exception as e:
                            continue
                            
                    self.total_files += 1
                    if self.total_files % 100 == 0:
                        # 更新进度
                        if self.callback:
                            self.callback({'progress': self.total_files})
        
        # 扫描完成
        if self.callback:
            self.callback({'finished': True})
    
    def stop(self):
        self.running = False
    
    @staticmethod
    def format_size(size_bytes):
        """格式化文件大小为人类可读形式"""
        for unit in ['B', 'KB', 'MB', 'GB']:
            if size_bytes < 1024.0:
                return f"{size_bytes:.2f} {unit}"
            size_bytes /= 1024.0
        return f"{size_bytes:.2f} TB"

class MobileScannerApp:
    """移动版文件扫描应用（使用tkinter）"""
    def __init__(self, root):
        self.root = root
        self.root.title("文件扫描工具")
        # 设置窗口大小为手机风格
        self.root.geometry("480x800")
        # 禁用窗口调整大小
        self.root.resizable(False, False)
        
        # 设置中文字体
        set_chinese_font(root)
        
        # 初始化变量
        self.scan_thread = None
        self.files = []
        self.filtered_files = []
        self.selected_extensions = []
        self.db_path = 'file_notes.db'
        self.init_db()
        
        # 创建主布局
        self.main_frame = tk.Frame(root, padx=10, pady=10)
        self.main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建顶部控制区
        self.create_control_area()
        
        # 创建文件列表区
        self.create_file_list_area()
        
        # 创建状态栏
        self.create_status_bar()
    
    def init_db(self):
        """初始化数据库"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute('''CREATE TABLE IF NOT EXISTS file_notes
                         (path TEXT PRIMARY KEY, note TEXT)''')
            conn.commit()
            conn.close()
        except Exception as e:
            messagebox.showerror("数据库错误", f"初始化数据库失败: {str(e)}")
    
    def create_control_area(self):
        """创建控制区域"""
        control_frame = tk.Frame(self.main_frame)
        control_frame.pack(fill=tk.X, pady=5)
        
        # 路径选择
        path_frame = tk.Frame(control_frame)
        path_frame.pack(fill=tk.X, pady=5)
        
        self.path_var = tk.StringVar()
        self.path_entry = tk.Entry(path_frame, textvariable=self.path_var, width=30)
        self.path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        
        browse_btn = tk.Button(path_frame, text="浏览", command=self.browse_directory, width=10)
        browse_btn.pack(side=tk.RIGHT, padx=2)
        
        # 文件类型选择（可滚动）
        type_frame = tk.LabelFrame(control_frame, text="文件类型")
        type_frame.pack(fill=tk.X, pady=5)
        
        type_canvas = tk.Canvas(type_frame, height=120)
        type_scrollbar = ttk.Scrollbar(type_frame, orient="vertical", command=type_canvas.yview)
        self.type_frame_inner = tk.Frame(type_canvas)
        
        self.type_frame_inner.bind(
            "<Configure>",
            lambda e: type_canvas.configure(scrollregion=type_canvas.bbox("all"))
        )
        
        type_canvas.create_window((0, 0), window=self.type_frame_inner, anchor="nw")
        type_canvas.configure(yscrollcommand=type_scrollbar.set)
        
        type_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        type_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 创建文件类型复选框
        self.file_types = {
            # 文档类型
            '.ppt': 'PPT',
            '.pptx': 'PPTX',
            '.doc': 'DOC',
            '.docx': 'DOCX',
            '.pdf': 'PDF',
            '.xls': 'XLS',
            '.xlsx': 'XLSX',
            # 代码文件类型
            '.py': 'Python',
            # 图片类型
            '.jpg': 'JPG',
            '.jpeg': 'JPEG',
            '.png': 'PNG',
            '.gif': 'GIF',
            '.bmp': 'BMP',
            # 视频类型
            '.mp4': 'MP4',
            '.avi': 'AVI',
            '.mov': 'MOV',
            '.wmv': 'WMV'
        }
        
        self.check_vars = {}
        self.checkboxes = {}
        
        # 3列布局
        col, row = 0, 0
        for ext, name in self.file_types.items():
            self.check_vars[ext] = tk.BooleanVar()
            checkbox = tk.Checkbutton(
                self.type_frame_inner, 
                text=name, 
                variable=self.check_vars[ext],
                command=lambda e=ext: self.on_checkbox_change(e)
            )
            checkbox.grid(row=row, column=col, sticky=tk.W, padx=5, pady=2)
            self.checkboxes[ext] = checkbox
            
            col += 1
            if col >= 3:
                col = 0
                row += 1
        
        # 扫描按钮
        scan_frame = tk.Frame(control_frame)
        scan_frame.pack(fill=tk.X, pady=5)
        
        self.scan_btn = tk.Button(scan_frame, text="开始扫描", command=self.start_scan, bg="#4CAF50", fg="white")
        self.scan_btn.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        
        self.stop_btn = tk.Button(scan_frame, text="停止扫描", command=self.stop_scan, state=tk.DISABLED, bg="#F44336", fg="white")
        self.stop_btn.pack(side=tk.RIGHT, fill=tk.X, expand=True, padx=2)
        
        # 搜索框
        search_frame = tk.Frame(control_frame)
        search_frame.pack(fill=tk.X, pady=5)
        
        self.search_var = tk.StringVar()
        self.search_var.trace_add("write", self.on_search_text_change)
        search_entry = tk.Entry(search_frame, textvariable=self.search_var, width=40)
        search_entry.pack(fill=tk.X, expand=True, padx=2)
    
    def create_file_list_area(self):
        """创建文件列表区域"""
        list_frame = tk.Frame(self.main_frame)
        list_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        # 创建列表视图（使用Treeview模拟）
        columns = ("name", "ext", "size", "date")
        self.file_tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=15)
        
        # 设置列宽和标题
        self.file_tree.heading("name", text="文件名")
        self.file_tree.heading("ext", text="类型")
        self.file_tree.heading("size", text="大小")
        self.file_tree.heading("date", text="日期")
        
        self.file_tree.column("name", width=200)
        self.file_tree.column("ext", width=60)
        self.file_tree.column("size", width=80, anchor=tk.E)
        self.file_tree.column("date", width=120)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(list_frame, orient="vertical", command=self.file_tree.yview)
        self.file_tree.configure(yscroll=scrollbar.set)
        
        # 放置Treeview和滚动条
        self.file_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 绑定双击事件
        self.file_tree.bind("<<TreeviewSelect>>", self.on_file_select)
        self.file_tree.bind("<Double-1>", lambda e: self.open_file_from_list())
    
    def create_status_bar(self):
        """创建状态栏"""
        status_frame = tk.Frame(self.main_frame, height=60)
        status_frame.pack(fill=tk.X, side=tk.BOTTOM, pady=5)
        
        # 状态标签
        status_label_frame = tk.Frame(status_frame)
        status_label_frame.pack(fill=tk.X, pady=2)
        
        tk.Label(status_label_frame, text="状态: ").pack(side=tk.LEFT)
        self.status_var = tk.StringVar(value="就绪")
        tk.Label(status_label_frame, textvariable=self.status_var).pack(side=tk.LEFT)
        
        # 文件计数
        count_label_frame = tk.Frame(status_frame)
        count_label_frame.pack(fill=tk.X, pady=2)
        
        tk.Label(count_label_frame, text="找到: ").pack(side=tk.LEFT)
        self.count_var = tk.StringVar(value="0")
        tk.Label(count_label_frame, textvariable=self.count_var).pack(side=tk.LEFT)
        tk.Label(count_label_frame, text=" 个文件").pack(side=tk.LEFT)
    
    def browse_directory(self):
        """浏览选择目录"""
        directory = filedialog.askdirectory(title="选择扫描目录")
        if directory:
            self.path_var.set(directory)
    
    def on_checkbox_change(self, ext):
        """处理复选框变化"""
        if self.check_vars[ext].get() and ext not in self.selected_extensions:
            self.selected_extensions.append(ext)
        elif not self.check_vars[ext].get() and ext in self.selected_extensions:
            self.selected_extensions.remove(ext)
    
    def on_search_text_change(self, *args):
        """处理搜索文本变化"""
        search_text = self.search_var.get()
        self.filter_files(search_text)
    
    def start_scan(self):
        """开始扫描"""
        path = self.path_var.get()
        if not path or not os.path.isdir(path):
            messagebox.showwarning("路径错误", "请选择有效的扫描目录")
            return
    
        # 检查是否选择了文件类型
        if not self.selected_extensions:
            messagebox.showwarning("未选择文件类型", "请至少选择一种文件类型进行扫描")
            return
    
        # 清空文件列表
        for item in self.file_tree.get_children():
            self.file_tree.delete(item)
        self.files = []
        self.filtered_files = []
        self.count_var.set("0")
    
        # 启动扫描线程
        self.scan_thread = ScanThread([path], self.selected_extensions, self.on_file_found)
        
        # 更新UI状态
        self.scan_btn.config(state=tk.DISABLED)
        self.stop_btn.config(state=tk.NORMAL)
        
        # 显示扫描状态
        type_names = [self.file_types.get(ext, ext) for ext in self.selected_extensions]
        self.status_var.set(f"正在扫描{"、".join(type_names)}文件...")
        
        self.scan_thread.start()
    
    def stop_scan(self):
        """停止扫描"""
        if self.scan_thread and self.scan_thread.is_alive():
            self.scan_thread.stop()
            self.status_var.set("正在停止扫描...")
    
    def on_file_found(self, data):
        """处理找到的文件"""
        if 'progress' in data:
            # 更新进度
            pass
        elif 'finished' in data:
            # 扫描完成
            self.root.after(0, self.update_scan_complete)
        else:
            # 找到新文件
            self.files.append(data)
            # 自动过滤
            search_text = self.search_var.get()
            if search_text:
                self.filter_files(search_text)
            else:
                self.filtered_files.append(data)
                # 在主线程更新UI
                self.root.after(0, lambda info=data: self.add_file_to_tree(info))
    
    def update_scan_complete(self):
        """更新扫描完成状态"""
        self.scan_btn.config(state=tk.NORMAL)
        self.stop_btn.config(state=tk.DISABLED)
        count = len(self.filtered_files)
        self.status_var.set(f"扫描完成，共找到 {count} 个文件")
    
    def add_file_to_tree(self, file_info):
        """添加文件到列表"""
        self.file_tree.insert("", tk.END, values=(
            file_info['name'],
            file_info['ext'],
            file_info['size'],
            file_info['created'][:10]
        ))
        # 更新计数
        self.count_var.set(str(len(self.filtered_files)))
    
    def filter_files(self, search_text):
        """过滤文件列表"""
        # 清空当前列表
        for item in self.file_tree.get_children():
            self.file_tree.delete(item)
        
        if not search_text.strip():
            self.filtered_files = self.files.copy()
            for file_info in self.files:
                self.file_tree.insert("", tk.END, values=(
                    file_info['name'],
                    file_info['ext'],
                    file_info['size'],
                    file_info['created'][:10]
                ))
        else:
            search_lower = search_text.lower()
            self.filtered_files = [f for f in self.files if search_lower in f['name'].lower()]
            for file_info in self.filtered_files:
                self.file_tree.insert("", tk.END, values=(
                    file_info['name'],
                    file_info['ext'],
                    file_info['size'],
                    file_info['created'][:10]
                ))
        
        # 更新计数
        self.count_var.set(str(len(self.filtered_files)))
    
    def on_file_select(self, event):
        """选择文件时显示右键菜单"""
        selected_items = self.file_tree.selection()
        if selected_items:
            # 创建右键菜单
            menu = tk.Menu(self.root, tearoff=0)
            
            # 打开文件
            menu.add_command(label="打开文件", command=self.open_file_from_list)
            
            # 添加/编辑备注
            menu.add_command(label="添加/编辑备注", command=self.add_note_from_list)
            
            # 复制文件路径
            menu.add_command(label="复制文件路径", command=self.copy_file_path_from_list)
            
            # 打开文件位置
            menu.add_command(label="打开文件位置", command=self.open_file_location_from_list)
            
            # 复制到自定义文件夹
            menu.add_command(label="复制到自定义文件夹", command=self.copy_file_from_list)
            
            # 文件改名
            menu.add_command(label="文件改名", command=self.rename_file_from_list)
            
            # 删除文件
            menu.add_command(label="删除文件", command=self.delete_file_from_list)
            
            # 显示菜单
            menu.post(event.x_root, event.y_root)
    
    def get_selected_file_info(self):
        """获取选中的文件信息"""
        selected_items = self.file_tree.selection()
        if not selected_items:
            return None
        
        item = selected_items[0]
        values = self.file_tree.item(item, "values")
        
        # 根据文件名查找完整的文件信息
        for file_info in self.filtered_files:
            if file_info['name'] == values[0] and file_info['ext'] == values[1]:
                return file_info
        
        return None
    
    def open_file_from_list(self):
        """从列表打开文件"""
        file_info = self.get_selected_file_info()
        if file_info:
            self.open_file(file_info)
    
    def open_file_location_from_list(self):
        """从列表打开文件位置"""
        file_info = self.get_selected_file_info()
        if file_info:
            self.open_file_location(file_info)
    
    def copy_file_path_from_list(self):
        """从列表复制文件路径"""
        file_info = self.get_selected_file_info()
        if file_info:
            self.copy_file_path(file_info)
    
    def add_note_from_list(self):
        """从列表添加备注"""
        file_info = self.get_selected_file_info()
        if file_info:
            self.add_note(file_info)
    
    def copy_file_from_list(self):
        """从列表复制到自定义文件夹"""
        file_info = self.get_selected_file_info()
        if file_info:
            self.copy_to_custom_folder(file_info)
    
    def rename_file_from_list(self):
        """从列表进行文件改名"""
        file_info = self.get_selected_file_info()
        if file_info:
            self.rename_file(file_info)
    
    def delete_file_from_list(self):
        """从列表删除文件"""
        file_info = self.get_selected_file_info()
        if file_info:
            self.delete_file(file_info)
    
    def open_file(self, file_info):
        """打开文件"""
        try:
            if sys.platform == 'win32':
                os.startfile(file_info['path'])
            elif sys.platform == 'darwin':  # macOS
                subprocess.run(['open', file_info['path']])
            else:  # Linux
                subprocess.run(['xdg-open', file_info['path']])
            self.status_var.set("文件已打开")
        except Exception as e:
            messagebox.showerror("操作失败", f"无法打开文件: {str(e)}")
    
    def open_file_location(self, file_info):
        """打开文件所在目录"""
        try:
            path = file_info['path']
            directory = os.path.dirname(path)
            
            if sys.platform == 'win32':
                os.startfile(directory)
            elif sys.platform == 'darwin':  # macOS
                subprocess.run(['open', directory])
            else:  # Linux
                subprocess.run(['xdg-open', directory])
        except Exception as e:
            messagebox.showerror("操作失败", f"无法打开文件位置: {str(e)}")
    
    def copy_file_path(self, file_info):
        """复制文件路径"""
        try:
            # 将路径复制到剪贴板
            self.root.clipboard_clear()
            self.root.clipboard_append(file_info['path'])
            self.status_var.set("文件路径已复制到剪贴板")
        except:
            messagebox.showinfo("提示", f"文件路径: {file_info['path']}")
    
    def add_note(self, file_info):
        """添加或编辑备注"""
        note = self.get_note(file_info['path'])
        
        # 创建备注编辑窗口
        note_window = tk.Toplevel(self.root)
        note_window.title("添加备注")
        note_window.geometry("400x300")
        note_window.resizable(False, False)
        
        # 文件名标签
        tk.Label(note_window, text=f"文件: {file_info['name']}").pack(padx=10, pady=5)
        
        # 备注输入框
        note_text = scrolledtext.ScrolledText(note_window, wrap=tk.WORD, width=45, height=12)
        note_text.insert(tk.END, note)
        note_text.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)
        
        # 按钮布局
        button_frame = tk.Frame(note_window)
        button_frame.pack(fill=tk.X, padx=10, pady=5)
        
        def save_note():
            new_note = note_text.get(1.0, tk.END).strip()
            self.save_note(file_info['path'], new_note)
            self.status_var.set("备注已保存")
            note_window.destroy()
        
        tk.Button(button_frame, text="保存", command=save_note).pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="取消", command=note_window.destroy).pack(side=tk.RIGHT, padx=5)
    
    def copy_to_custom_folder(self, file_info):
        """复制文件到自定义文件夹"""
        dest_folder = filedialog.askdirectory(title="选择目标文件夹", initialdir=os.path.dirname(file_info['path']))
        
        if not dest_folder:
            return
        
        try:
            source_path = file_info['path']
            dest_path = os.path.join(dest_folder, file_info['name'])
            
            # 如果文件已存在，询问是否覆盖
            if os.path.exists(dest_path):
                if not messagebox.askyesno("文件已存在", f"文件 '{file_info['name']}' 已存在。是否覆盖？"):
                    return
            
            shutil.copy2(source_path, dest_path)
            messagebox.showinfo("复制成功", f"文件已成功复制到目标文件夹")
        except Exception as e:
            messagebox.showerror("复制失败", f"无法复制文件: {str(e)}")
    
    def rename_file(self, file_info):
        """重命名文件"""
        new_name = simpledialog.askstring("文件重命名", "请输入新文件名（包含扩展名）:", initialvalue=file_info['name'])
        
        if not new_name:
            return
        
        if new_name == file_info['name']:
            return
        
        try:
            file_path = file_info['path']
            directory = os.path.dirname(file_path)
            new_file_path = os.path.join(directory, new_name)
            
            if os.path.exists(new_file_path):
                messagebox.showerror("重命名失败", f"文件名 '{new_name}' 已存在！")
                return
            
            os.rename(file_path, new_file_path)
            
            # 更新文件信息
            file_info['name'] = new_name
            file_info['path'] = new_file_path
            
            # 刷新列表
            search_text = self.search_var.get()
            self.filter_files(search_text)
            
            self.status_var.set(f"文件已重命名为 '{new_name}'")
        except Exception as e:
            messagebox.showerror("重命名失败", f"无法重命名文件: {str(e)}")
    
    def delete_file(self, file_info):
        """删除文件"""
        if messagebox.askyesno("确认删除", f"确定要删除文件 '{file_info['name']}' 吗？此操作不可撤销。"):
            try:
                os.remove(file_info['path'])
                
                # 从列表中移除
                if file_info in self.files:
                    self.files.remove(file_info)
                if file_info in self.filtered_files:
                    self.filtered_files.remove(file_info)
                
                # 刷新列表
                search_text = self.search_var.get()
                self.filter_files(search_text)
                
                self.status_var.set(f"文件已成功删除")
            except Exception as e:
                messagebox.showerror("删除失败", f"无法删除文件: {str(e)}")
    
    def get_note(self, file_path):
        """从数据库获取备注"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute("SELECT note FROM file_notes WHERE path=?", (file_path,))
            result = c.fetchone()
            conn.close()
            return result[0] if result else ""
        except Exception as e:
            messagebox.showerror("数据库错误", f"获取备注失败: {str(e)}")
            return ""
    
    def save_note(self, file_path, note):
        """保存备注到数据库"""
        try:
            conn = sqlite3.connect(self.db_path)
            c = conn.cursor()
            c.execute("REPLACE INTO file_notes (path, note) VALUES (?, ?)", 
                     (file_path, note))
            conn.commit()
            conn.close()
        except Exception as e:
            messagebox.showerror("数据库错误", f"保存备注失败: {str(e)}")
    
    def on_closing(self):
        """窗口关闭事件处理"""
        if self.scan_thread and self.scan_thread.is_alive():
            if messagebox.askyesno("确认", "扫描正在进行中，确定要退出吗？"):
                self.scan_thread.stop()
                self.scan_thread.join(timeout=1)
                self.root.destroy()
        else:
            self.root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    app = MobileScannerApp(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()